<?php

/**
 * QPHP
 *
 * Copyright (c) 2022 QPHP
 *
 * @author lbnnbs
 * @copyright Copyright (c) 2022 lbnnbs QPHP (https://gitee.com/lbnnbs/qphp)
 * @license AGPLv3 License
 * 
 * Template 模板引擎
 * 
 * 说明中的...表示其他字符输出
 * 
 * 输出变量：{$var}或者{$arr['key']['key2']}
 * 
 * 输出PHP：{:...} 等同于 <?php echo ...; ?>，可用于输出函数结果: {:trim($a) . 'bb'} 等同于 <?php echo trim($a) . 'bb'; ?>
 * 
 * 条件判断：{if $do == '1'}...{elseif $op['key'] == '2'}...{else}...{/if}
 * 
 * 循环输出：{for $i 0 50}...{/for} 等同于 <?php for($i=0; $i<=50; $i++){...} ?>
 * 
 * 数组循环输出：{loop $list $item}...{$item['key']}...{/loop}
 * 
 * 数组循环输出带下标：{loop $list $key $item}...{$key}...{$item['key']}...{/loop}
 * 
 * 使用PHP：{php $var = 1;}
 * 
 * 包含文件：{template '../header'} 相对于当前文件路径的文件名，不需要带文件后缀，如果是以/开头，表示是一个以模板文件目录作为根目录的绝对路径
 * 
 * 包含文件带变量，支持变量赋值：{template '../header' a =1 c = 2 k= 'aa' f="me" a=$b}
 * 
 * 输出花括号：{##...##}
 * 
 * 输出文本或常量：{...}
 * 
 * 输出注释：<!--{...}-->
 * 
 * 原样输出：{literal} ... {/literal} 将直接输出 ... 的内容
 */
class Template {

    public static function template_compile($from, $to) {
        $path = dirname($to);
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }

        $content = self::template_parse(file_get_contents($from));
        file_put_contents($to, $content);
    }

    public static function template_parse($str) {

        // 原样输出，实际是转换所有大括号为{##
        $str = preg_replace_callback('/{literal}(.*?){\/literal}/s', function ($matchs) {
            if ($matchs[1]) {
                return preg_replace(array('/\{/s', '/\}/s',), array('{##', '##}'), $matchs[1]);
            }
        }, $str);

        // 包含文件，支持变量，使用方式：{template '../header' a =1 c = 2 k= 'aa' f="me" a=$b}
        $str = preg_replace_callback('/{(template\s+[\'\"].+?[\'\"])(.*?)}/', function ($matchs) {
            // $matchs[1]中就已经包含了多个{template标签的参数串结果
            // 对参数串结果进行替换，形成变量赋值语句
            $varexp = '';
            if ($matchs[2]) {
                // 匹配数字、变量、字符串3种变量值
                preg_match_all('/(\s+\w+?\s*?=\s*?\d+)|(\s+\w+?\s*?=\s*?\$\w+\s*?)|(\s+\w+?\s*?=\s*?[\'\"]\S+[\'\"]\s*?)/', $matchs[2], $subs); // 匹配变量赋值
                if ($subs) {
                    foreach ($subs[0] as $sub) {
                        $kv = explode('=', $sub);
                        $varexp .= '($' . trim($kv[0]) . '=' . trim($kv[1]) . ') ; ';
                    }
                }
            }

            $tpl = '';
            if ($matchs[1]) {
                //template '../header'
                $tpl = preg_replace('/template\s+[\'\"](.+?)[\'\"]/', '<?php ' . $varexp . ' include $this->template(\'$1\');?>', $matchs[1]);
            }
            return $tpl;
        }, $str);

        // 注释
        $str = preg_replace('/<!--{(.+?)}-->/s', '{$1}', $str);
        // 执行php
        $str = preg_replace('/{php\s+(.+?)}/', '<?php $1?>', $str);
        // if else elseif
        $str = preg_replace('/{if\s+(.+?)}/', '<?php if($1) { ?>', $str);
        $str = preg_replace('/{else}/', '<?php } else { ?>', $str);
        $str = preg_replace('/{else ?if\s+(.+?)}/', '<?php } else if($1) { ?>', $str);
        $str = preg_replace('/{\/if}/', '<?php } ?>', $str);
        // for循环 {for $i 0 50}
        $str = preg_replace('/{for\s+(\S+)\s+(\S+)\s+(\S+)}/', '<?php for($1 = $2; $1 <= $3; $1++) { ?>', $str);
        $str = preg_replace('/{\/for}/', '<?php } ?>', $str);
        // loop循环 {loop $list $item}  {loop $list $index $item}
        $str = preg_replace('/{loop\s+(\S+)\s+(\S+)}/', '<?php if(is_array($1)) { foreach($1 as $2) { ?>', $str);
        $str = preg_replace('/{loop\s+(\S+)\s+(\S+)\s+(\S+)}/', '<?php if(is_array($1)) { foreach($1 as $2 => $3) { ?>', $str);
        $str = preg_replace('/{\/loop}/', '<?php } } ?>', $str);
        // echo php {:...}
        $str = preg_replace('/{\:(.*?)}/', '<?php echo $1;?>', $str);
        // echo 变量 {$var}
        $str = preg_replace('/{(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)}/', '<?php echo $1;?>', $str);
        $str = preg_replace('/{(\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\[\]\'\"\$]*)}/', '<?php echo $1;?>', $str);
        // 直接使用 <?php 
        $str = preg_replace_callback('/<\?php([^\?]+)\?>/s', function ($matchs) {
            $code = "<?php {$matchs[1]}?>";
            $code = preg_replace('/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\](?![a-zA-Z0-9_\-\.\x7f-\xff\[\]]*[\'"])/s', "['$1']", $code);
            return str_replace('\\\"', '\"', $code);
        }, $str);
        // echo 文本 {abc123}
        $str = preg_replace('/{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)}/s', '<?php echo $1;?>', $str);
        // echo {$order} 使用 {##$order##}
        $str = str_replace('{##', '{', $str);
        $str = str_replace('##}', '}', $str);
        return $str;
    }

}
